﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text.Json.Serialization;
using DimensionsHelper.Common.Contracts.Excel;
using DimensionsHelper.Common.Contracts.Excel.Options;
using DimensionsHelper.Common.Utils;

namespace DimensionsHelper.Common.Contracts.TableFormatter.Options;

/// <summary>
///     显著性字母样式配置。包含了合并字母和不合并字母的样式，合并字母时，包含隐藏列字母和不隐藏列字母的样式。
/// </summary>
[Serializable]
public class SigCharOptions
{
    /// <summary>
    ///     是否将Sig字母合并进数据单元格。
    /// </summary>
    public bool Merge { get; set; }

    /// <summary>
    ///     是否隐藏Sig列标记行。
    /// </summary>
    public bool HideSigColumnMark { get; set; }

    /// <summary>
    ///     获取或设置是否隐藏数据单元格的显著性字母，只有在<see cref="Merge" />为true时才有用。
    /// </summary>
    public bool HideSigCellCharacter { get; set; }

    /// <summary>
    ///     获取或设置Sig两列之间数据的差值边界
    /// </summary>
    public double GapBoundary { get; set; }

    /// <summary>
    ///     获取或设置Sig两列之间数据差值边界的类型
    /// </summary>
    public SigGapBoundaryType GapBoundaryType { get; set; } = SigGapBoundaryType.Greater;

    /// <summary>
    ///     获取或设置小写Sig字母的样式，只有在<see cref="Merge" />为true时才有用。
    /// </summary>
    public FontStyleOptions LowerCaseStyleOptions { get; set; } = new();

    /// <summary>
    ///     获取或设置大写Sig字母的字体样式，只有在<see cref="Merge" />为true时才有用。
    /// </summary>
    public FontStyleOptions UpperCaseStyleOptions { get; set; } = new();

    /// <summary>
    ///     获取或设置Sig列标记字母行的单元格样式。
    /// </summary>
    public RangeStyleOptions SigMarkRowStyleOptions { get; set; } = new TableOptions();

    /// <summary>
    ///     获取或设置Sig Test结果单元格样式，只有在<see cref="Merge" />为false时才有用。
    ///     <list type="number">
    ///         <item>如果<see cref="Merge" /> = true，此样式影响合并后包含显著性字母的单元格。</item>
    ///         <item>如果<see cref="Merge" /> = false，此样式只影响显著性字母单元格。</item>
    ///     </list>
    /// </summary>
    public RangeStyleOptions SigResultStyleOptions { get; set; } = new TableOptions();

    /// <summary>
    ///     获取或设置显著性列对比配置表。
    /// </summary>
    public SigComparisonMap ComparisonMap { get; set; } = new();
}


[JsonConverter(typeof(JsonStringEnumConverter<SigGapBoundaryType>))]
public enum SigGapBoundaryType
{
    /// <summary>
    ///     不比较数据差值
    /// </summary>
    [Description("不比较")]
    None,

    /// <summary>
    ///     指示比较数据时，大于边界值时返回true。
    /// </summary>
    [Description("大于")]
    Greater,

    /// <summary>
    ///     指示比较数据时，大于等于边界值时返回true。
    /// </summary>
    [Description("大于等于")]
    GreaterEqual,

    /// <summary>
    ///     指示比较数据时，小于边界值时返回true。
    /// </summary>
    [Description("小于")]
    Less,

    /// <summary>
    ///     指示比较数据时，小于等于边界值时返回true。
    /// </summary>
    [Description("小于等于")]
    LessEqual
}


/// <summary>
///     显著性标记颜色的样式类型。
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter<SigComparisonMarkType>))]
public enum SigComparisonMarkType
{
    /// <summary>
    ///     不配置
    /// </summary>
    [Description("未设置")]
    None,

    /// <summary>
    ///     颜色标记在文字上
    /// </summary>
    [Description("文字")]
    MarkAtCharacter,

    /// <summary>
    ///     颜色标记在边框上
    /// </summary>
    [Description("边框")]
    MarkAtBorder,

    /// <summary>
    ///     颜色标记在背景颜色
    /// </summary>
    [Description("背景填充")]
    MarkAtInterior
}


/// <summary>
///     当表格边框标记和相邻单元格冲突时，使用此枚举Flag指定保留边框位置。
/// </summary>
[Flags]
[JsonConverter(typeof(JsonStringEnumConverter<SigComparisonBorderMarkConflictFlags>))]
public enum SigComparisonBorderMarkConflictFlags
{
    /// <summary>
    ///     默认值，保留上边框和右边框
    /// </summary>
    [Description("默认")]
    Default = 0,

    /// <summary>
    ///     当边框和左侧单元格右边框冲突时，保留左边框
    /// </summary>
    [Description("左")]
    Left = 0x1,

    /// <summary>
    ///     当边框和上方单元格下边框冲突时，保留上边框
    /// </summary>
    [Description("上")]
    Top = 0x2,

    /// <summary>
    ///     当边框和右侧单元格左边框冲突时，保留右边框
    /// </summary>
    [Description("右")]
    Right = 0x4,

    /// <summary>
    ///     当边框和下方单元格上边框冲突时，保留下边框
    /// </summary>
    [Description("下")]
    Bottom = 0x8
}


/// <summary>
///     显著性对比配置。此配置只用于隐藏显著性字母时的配置。
/// </summary>
/// <remarks>
///     隐藏字母时，分两种标记颜色：字母标记在基础列还是对比列。
/// </remarks>
[Serializable]
public class SigComparison
{
    /// <summary>
    ///     显著性检验对比第一列
    /// </summary>
    public char MarkColumn { get; set; } = char.MinValue;

    /// <summary>
    ///     显著性检验对比第二列
    /// </summary>
    public char AnotherColumn { get; set; } = char.MinValue;

    /// <summary>
    ///     获取或设置忽略的起始显著性字母，如果表格是显著性检验表格并且表格的列显著性字母起始字母在
    ///     此属性字符内，这个表格的标注将被忽略。
    /// </summary>
    public string StartCharactersToSkip { get; set; } = string.Empty;

    /// <summary>
    ///     颜色标注类型 - 标注在文字上或者边框上。
    /// </summary>
    public SigComparisonMarkType MarkType { get; set; } = SigComparisonMarkType.MarkAtCharacter;

    /// <summary>
    ///     获取或设置相邻单元格边框冲突时保留边框位置。
    /// </summary>
    public SigComparisonBorderMarkConflictFlags BorderMarkConflictFlags { get; set; } =
        SigComparisonBorderMarkConflictFlags.Default;

    /// <summary>
    ///     当显著性字母在<see cref="MarkColumn" />列时，<see cref="MarkColumn" />列标注的颜色。
    /// </summary>
    public ExcelStyleColor CharAtMarkColumnColor { get; set; } = ExcelStyleColor.Empty;

    /// <summary>
    ///     当显著性字母不在<see cref="MarkColumn" />列时，<see cref="MarkColumn" />列标注的颜色。
    /// </summary>
    public ExcelStyleColor CharAtAnotherColumnColor { get; set; } = ExcelStyleColor.Empty;

    /// <summary>
    ///     当前的显著性检测是否有效 - 如果两列都不是<see cref="string.Empty" />且不相同才有效
    /// </summary>
    public bool IsValid => MarkColumn != char.MinValue
                           && AnotherColumn != char.MinValue
                           && !MarkColumn.Equals(AnotherColumn)
                           && (CharAtAnotherColumnColor != ExcelStyleColor.Empty || CharAtMarkColumnColor != ExcelStyleColor.Empty);

    /// <summary>
    ///     显著性表达式，值是"<see cref="MarkColumn" />+<see cref="AnotherColumn" />"
    /// </summary>
    public string ExpressionA => $"{MarkColumn}{AnotherColumn}";

    /// <summary>
    ///     显著性表达式，值是"<see cref="AnotherColumn" />+<see cref="MarkColumn" />"
    /// </summary>
    public string ExpressionB => $"{AnotherColumn}{MarkColumn}";
}

/// <summary>
///     显著性标记配置表。
/// </summary>
[Serializable]
public class SigComparisonMap
{
    private readonly Dictionary<string, int> _exprToIndex = new();

    /// <summary>
    ///     每两列的对比配置。
    /// </summary>
    public List<SigComparison> Comparisons { get; set; } = [];

    private void UpdateIndex()
    {
        if (_exprToIndex.Count == Comparisons.Count * 2)
        {
            return;
        }

        _exprToIndex.Clear();

        for (int i = 0; i < Comparisons.Count; i++)
        {
            SigComparison comp = Comparisons[i];
            _exprToIndex.TryAdd(comp.ExpressionA, i);
            _exprToIndex.TryAdd(comp.ExpressionB, i);
        }
    }

    /// <summary>
    ///     使用显著性表达式查找对应的配置。
    /// </summary>
    /// <param name="expr">显著性列表达式，应该是两个英文字母。</param>
    /// <returns>查找到的显著性配置，如果表达式未配置，返回null。</returns>
    public SigComparison? GetByExpression(string expr)
    {
        UpdateIndex();
        return _exprToIndex.TryGetValue(expr, out int comp) ? Comparisons[comp] : null;
    }

    /// <summary>
    ///     使用显著性列定义字母查找对应的配置。检索时，不区分columnA和columnB的顺序。
    /// </summary>
    /// <param name="columnA">对比第一列</param>
    /// <param name="columnB">对比第二列</param>
    /// <returns>查找到的显著性配置，如果表达式未配置，返回null。</returns>
    public SigComparison? GetByColumns(string columnA, string columnB)
    {
        return GetByExpression(columnA + columnB);
    }

    /// <summary>
    ///     使用显著性列定义字母查找对应的配置。检索时，不区分columnA和columnB的顺序。
    /// </summary>
    /// <param name="columnA">对比第一列</param>
    /// <param name="columnB">对比第二列</param>
    /// <returns>查找到的显著性配置，如果表达式未配置，返回null。</returns>
    public SigComparison? GetByColumns(char columnA, char columnB)
    {
        return GetByExpression($"{columnA}{columnB}");
    }

    /// <summary>
    ///     尝试向集合中添加新的对比配置。
    /// </summary>
    /// <param name="columnA">对比基列</param>
    /// <param name="columnB">对比列</param>
    /// <param name="comparison">添加的对比列配置，如果指定列已存在，此值是null。</param>
    /// <returns>如果成功添加，返回true，否则，返回false。</returns>
    public bool TryAdd(string columnA, string columnB, out SigComparison comparison)
    {
        comparison = null!;

        if (string.IsNullOrEmpty(columnA) || string.IsNullOrEmpty(columnB))
        {
            return false;
        }

        if (!char.IsLetter(columnA[0]) || !char.IsLetter(columnB[0]))
        {
            return false;
        }

        var comp = GetByColumns(columnA, columnB);

        if (comp != null)
        {
            return false;
        }

        comparison = new SigComparison
        {
            MarkColumn = columnA[0],
            AnotherColumn = columnB[0]
        };

        _exprToIndex.Add(columnA + columnB, Comparisons.Count);
        _exprToIndex.Add(columnB + columnA, Comparisons.Count);
        Comparisons.Add(comparison);

        return true;
    }

    /// <inheritdoc cref="TryAdd(string, string, out SigComparison)" />
    public bool TryAdd(char columnA, char columnB, out SigComparison comparison)
    {
        return TryAdd(columnA.ToString(), columnB.ToString(), out comparison);
    }
}